package cn.neo.hhrpc.core.config;


import cn.neo.hhrpc.core.provider.ProviderBootstrap;
import cn.neo.hhrpc.core.provider.ProviderInvoker;
import cn.neo.hhrpc.core.registry.RegistryCenter;
import cn.neo.hhrpc.core.registry.hh.HHRegistryCenter;
import cn.neo.hhrpc.core.registry.zk.ZkRegistryCenter;
import cn.neo.hhrpc.core.transport.SpringBootTransport;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.annotation.Order;

@Slf4j
@Configuration
@Import({AppConfigProperties.class, ProviderConfigProperties.class, SpringBootTransport.class})
public class ProviderConfig {

    @Value("${server.port:8080}")
    private String port;

//    @Bean
//    @ConditionalOnMissingBean
//    @ConditionalOnProperty(prefix = "apollo.bootstrap", value = "enabled")
//    ApolloChangedListener provider_apolloChangedListener() {
//        return new ApolloChangedListener();
//    }

    @Bean
    ProviderInvoker providerInvoker(@Autowired ProviderBootstrap providerBootstrap) {
        return new ProviderInvoker(providerBootstrap);
    }

    /**
     * 将ProviderBootstrap放入Spring容器中
     *
     * @return
     */
    @Bean
    ProviderBootstrap providerBootstrap(@Autowired AppConfigProperties appConfigProperties,
                                        @Autowired ProviderConfigProperties providerConfigProperties) {
        return new ProviderBootstrap(port, appConfigProperties, providerConfigProperties);
    }

    @Bean
    // 启动类中的ApplicationRunner优先级高于此处，会导致userService未被加载到容器中就使用，所以要优先启动此处的ApplicationRunner用于加载userService到容器中，防止空指针
    @Order(Integer.MIN_VALUE)
    public ApplicationRunner providerBootstrap_runner(@Autowired ProviderBootstrap providerBootstrap) {
        return x -> {
            log.info("providerBootstrap starting....");
            providerBootstrap.start();
            log.info("providerBootstrap ending....");
        };
    }

    // @Bean(initMethod = "start", destroyMethod = "stop")
    // 此处调用 start 和 stop 方法为 zk 的启动和注销，会导致 provider 中的 stop 方法不会执行，需要改为在 provider 中启动注册和注销，
    // 因为@PreDestroy的stop的执行顺序比zk的stop晚。这样会导致 provider stop 的时候拿不到活着的 zk 实例并抛出异常(此处异常不会抛出，因为在执行zk注销的时候执行了quietly()方法，会把异常吞掉，让 zk 在内部处理)
    @Bean
    // 当具有相同类型的bean注册时(consumer也会注册)，它会保证当前实例只有一个
    @ConditionalOnMissingBean
    public RegistryCenter provider_rc() {
        return new HHRegistryCenter();
    }
}
